package pers.bc.utils.pub;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamSource;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import pers.bc.utils.constant.IPubCons;
import pers.bc.utils.constant.IPubEvnCons;
import pers.bc.utils.file.FileUtilbc;
import pers.bc.utils.throwable.ExceptionUtilbc;

/**
 * @version 1.0<br>
 * @qualiFild nc.pub.itf.tools.pub.XMLUtils.java<br>
 * @author：licheng<br>
 * @date Created on 2019-8-12<br>
 */
public final class XmlUtil
{

    public static String xmlHead = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";

    private static TransformerFactory transformerFactory;
    private static final SAXTransformerFactory saxFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();

    /**
     * *********************************************************** <br>
     * *说明： 压缩xml<br>
     * @see <br>
     * @param xmlStr
     * @return <br>
     * @String <br>
     * @methods pers.bc.utils.pub.XmlUtil#compressXml <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-17 <br>
     * @time 上午2:00:42 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static String compressXml(String xmlStr)
    {
        return xmlStr.replace(IPubCons._N, IPubCons.EMPTY)//
                .replace(IPubCons.TAB, IPubCons.EMPTY)//
                .replace(IPubCons.RETURN, IPubCons.EMPTY);
    }

    /**
     * *********************************************************** <br>
     * *说明： Map轉xml字符串 <br>
     * @see <br>
     * @param dateMap
     * @return
     * @throws Exception <br>
     * @String <br>
     * @methods pers.bc.utils.pub.XmlUtil#map2Xml <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-15 <br>
     * @time 上午9:38:52 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static String map2Xml(Map<String, Object> dateMap) throws Exception
    {
        return map2Xml(dateMap, 0);
    }

    private static String map2Xml(Map<String, Object> dateMap, Integer tabIdx) throws Exception
    {
        StringBuffer xml = new StringBuffer();
        Iterator<String> iterator = dateMap.keySet().iterator();
        Iterator<String> itrSub = dateMap.keySet().iterator();
        int size = dateMap.keySet().size();
        // JudgeAssertUtil.checkAssert((tabIdx == 0 && size > 1), "XML转换失败！Root的MAP（key-value）只能存在一组，请确认！");
        if (1 == size)
        {
            String key = iterator.next();
            if (key.startsWith(PubEnvUtil.DASH)) xml.append(IPubCons.SPACE)//
                    .append(key.replace(IPubCons.DASH, PubEnvUtil.EMPTY))//
                    .append(IPubCons.EQUALS).append(IPubCons.QUOTE).append(dateMap.get(key)).append(IPubCons.QUOTE)//
                    .append(IPubCons.RIGHT_CHEV).append(IPubCons._N);
            else
                xml.append(formatXML(dateMap, itrSub.next(), tabIdx));
        }
        else
        {
            while (iterator.hasNext())
            {
                String key = iterator.next();
                if (key.startsWith(PubEnvUtil.DASH)) xml.append(IPubEvnCons.SPACE)//
                        .append(key.replace(PubEnvUtil.DASH, PubEnvUtil.EMPTY)).append(IPubCons.EQUALS)//
                        .append(IPubCons.QUOTE).append(dateMap.get(key)).append(IPubCons.QUOTE);
            }
            if (tabIdx > 0) xml.append(IPubCons.RIGHT_CHEV).append(IPubCons._N);
            while (itrSub.hasNext())
                xml.append(formatXML(dateMap, itrSub.next(), tabIdx));
        }

        return StringUtil.valueOfEmpty(xml);
    }

    private static String formatXML(Map<String, Object> dateMap, String key, Integer tabIdx) throws Exception
    {
        StringBuffer xmlSub = new StringBuffer();
        if (!key.startsWith(PubEnvUtil.DASH))
        {
            addTab(xmlSub, tabIdx);
            xmlSub.append(IPubCons.LEFT_CHEV).append(key);
            Object value = dateMap.get(key);
            if (value instanceof Map)
            {
                xmlSub.append(map2Xml((Map<String, Object>) value, tabIdx + 1));
                addTab(xmlSub, tabIdx);
            }
            else if (value instanceof Collection)
            {
                xmlSub.append(IPubCons.RIGHT_CHEV).append(IPubCons._N).append(map2Xml((Collection<Object>) value, tabIdx + 1));
                addTab(xmlSub, tabIdx);
            }
            else
                xmlSub.append(IPubCons.RIGHT_CHEV).append(value);

            xmlSub.append(IPubCons.LEFT_CHEV).append(IPubCons.SLASH).append(key).append(IPubCons.RIGHT_CHEV).append(IPubCons._N);
        }

        return StringUtil.valueOfEmpty(xmlSub);
    }

    private static void addTab(StringBuffer xml, Integer tabIdx)
    {
        for (int i = 0; i < tabIdx; i++)
        {
            xml.append(IPubCons.TAB);
        }
    }

    /**
     * *********************************************************** <br>
     * *说明： <br>
     * @see <br>
     * @param colls
     * @param tabIdx
     * @return
     * @throws Exception <br>
     * @String <br>
     * @methods pers.bc.utils.pub.XmlUtil#map2Xml <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-15 <br>
     * @time 上午9:38:45 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    @SuppressWarnings("unchecked")
    private static String map2Xml(Collection<Object> colls, Integer tabIdx) throws Exception
    {
        StringBuffer xmlSub = new StringBuffer();
        // addTab(xmlSub, tabIdx);
        // 获取迭代器
        Iterator<Object> iterator = colls.iterator();
        // hasNext 检查集合中是否还有元素，如果有则进入循环。
        while (iterator.hasNext())
        {
            // 循环打印集合中的 元素
            Object obj = iterator.next();
            if (obj instanceof Map) xmlSub.append(map2Xml((Map<String, Object>) obj, tabIdx));
            else if (obj instanceof Collection) xmlSub.append(map2Xml((Collection<Object>) obj, tabIdx));
            else
            {
                addTab(xmlSub, tabIdx);
                xmlSub.append(obj).append(IPubCons.COMMA);
            }
        }
        String str = StringUtil.valueOfEmpty(xmlSub);
        if (str.endsWith(IPubCons.COMMA)) //
            str = (str.substring(0, str.length() - 1) + IPubCons.CRLF);

        return str;
    }

    /**
     * *********************************************************** <br>
     * *说明： 读取xml文件，返回map对象<br>
     * @see <br>
     * @param in 文件路径
     * @return map对象
     * @throws DocumentException <br>
     * @Map<String,Object> <br>
     * @methods pers.bc.utils.pub.XmlUtil#xmlToMap <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-15 <br>
     * @time 上午9:38:19 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static Map<String, Object> xmlToMap(InputStream in) throws DocumentException
    {
        // 创建saxReader对象
        SAXReader reader = new SAXReader();
        // 通过read方法读取一个文件 转换成Document对象
        Document document = reader.read(in);
        // 获取根节点元素对象
        Element node = document.getRootElement();
        // 遍历所有的元素节点
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        // 处理节点
        listNodes(node, map);

        return map;
    }

    /**
     * *********************************************************** <br>
     * *说明：遍历当前节点元素下面的所有(元素的)子节点 <br>
     * @see <br>
     * @param node
     * @param map <br>
     * @void <br>
     * @methods pers.bc.utils.pub.XmlUtil#listNodes <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-15 <br>
     * @time 上午9:38:10 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static void listNodes(Element node, Map<String, Object> map)
    {
        Map<String, Object> xiaoMap = new LinkedHashMap<String, Object>();
        String nodeKey = node.getName();
        // 获取当前节点的所有属性节点
        List<Attribute> list = node.attributes();
        // 遍历属性节点
        Map<String, Object> attrMap = new LinkedHashMap<String, Object>();
        for (Attribute attr : list)
        {
            attrMap.put(attr.getName(), attr.getValue());
        }
        // 20241117
        // if (PubEnvUtil.isEmptyObj(attrMap)) xiaoMap.put("attr", attrMap);

        // 当前节点下面子节点迭代器
        Iterator<Element> it = node.elementIterator();

        if (!("".equals(node.getTextTrim())) || !it.hasNext()) map.put(nodeKey, node.getTextTrim());
        else
        {
            // 不为空
            if (PubEnvUtil.isEmpty(StringUtil.valueOfEmpty(map.get(nodeKey)))) map.put(nodeKey, xiaoMap);
            else
            {
                List<Map<String, Object>> childList = null;
                // 获取原来的
                Object obj = map.get(nodeKey);
                if (obj instanceof Iterable)
                {
                    // 非第一个
                    childList = (List<Map<String, Object>>) obj;
                    childList.add(xiaoMap);
                }
                else if (obj instanceof Map)
                {
                    // 第一个
                    Map<String, Object> childMap = (Map<String, Object>) obj;
                    childList = new ArrayList();
                    childList.add(childMap);
                    childList.add(xiaoMap);
                }
                // 添加新的
                map.put(nodeKey, childList);
            }
        }

        // 遍历
        while (it.hasNext())
        {
            // 获取某个子节点对象
            Element e = it.next();
            // 对子节点进行遍历
            listNodes(e, xiaoMap);
        }
    }

    /*************************************************************
     * <br>
     *
     * 说明:是否是xml文档 <br>
     *
     * @Description <br>
     * @Date Created on 2020/9/29 15:13 <br>
     * @Param [rtnMsg] <br>
     * @return boolean <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static boolean isXmlDocument(String rtnMsg)
    {
        boolean flag = true;
        try
        {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
            builder.parse(new InputSource(new StringReader(rtnMsg)));
        }
        catch (Exception e)
        {
            flag = false;
        }

        return flag;
    }

    /**
     * *********************************************************** <br>
     * *说明： 将对象直接转换成String类型的 XML输出 <br>
     * 对象需要注解：@XmlRootElement
     * @see <br>
     * @param obj
     * @return <br>
     * @String <br>
     * @methods pers.bc.utils.pub.XmlUtil#convertToXml <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-14 <br>
     * @time 下午5:17:30 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static String convertToXml(Object obj)
    {
        // 创建输出流
        StringWriter sw = new StringWriter();
        try
        {
            // 利用jdk中自带的转换类实现
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            // 格式化xml输出的格式
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            // 将对象转换成输出流形式的xml
            marshaller.marshal(obj, sw);
        }
        catch (JAXBException e)
        {
            ExceptionUtilbc.throwException(e);
        }
        finally
        {
            FileUtilbc.close(sw);
        }

        return sw.toString();
    }

    /**
     * *********************************************************** <br>
     * *说明：将对象根据路径转换成xml文件 <br>
     * @see <br>
     * @param obj
     * @param path
     * @throws JAXBException
     * @throws IOException <br>
     * @void <br>
     * @methods pers.bc.utils.pub.XmlUtil#convertToXml <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-14 <br>
     * @time 下午5:14:35 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static void convertToXml(Object obj, String path) throws JAXBException, IOException
    {
        // 利用jdk中自带的转换类实现
        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        // 格式化xml输出的格式
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        // 将对象转换成输出流形式的xml
        // 创建输出流
        FileWriter fw = new FileWriter(path);
        marshaller.marshal(obj, fw);
    }

    /**
     * *********************************************************** <br>
     * *说明： 将String类型的xml转换成对象<br>
     * @see <br>
     * @param clazz
     * @param xmlStr
     * @return
     * @throws JAXBException <br>
     * @Object <br>
     * @methods pers.bc.utils.pub.XmlUtil#convertXmlStrToObject <br>
     * @author LiBencheng <br>
     * @date Created on 2024-11-14 <br>
     * @time 下午5:14:57 <br>
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public static Object convertXmlStrToObject(Class clazz, String xmlStr) throws JAXBException
    {

        JAXBContext context = JAXBContext.newInstance(clazz);
        // 进行将Xml转成对象的核心接口
        Unmarshaller unmarshaller = context.createUnmarshaller();
        StringReader sr = new StringReader(xmlStr);
        Object xmlObject = unmarshaller.unmarshal(sr);

        return xmlObject;
    }

    public static TransformerHandler createTransformerHandler() throws TransformerConfigurationException
    {
        return saxFactory.newTransformerHandler();
    }

    public static TransformerHandler createTransformerHandler(Templates template) throws TransformerConfigurationException
    {
        return saxFactory.newTransformerHandler(template);
    }

    public static Templates createTemplate(InputStream is) throws TransformerConfigurationException
    {
        return createTemplate(new StreamSource(is));
    }

    public static Templates createTemplate(File file) throws TransformerConfigurationException
    {
        return createTemplate(new StreamSource(file));
    }

    private static Templates createTemplate(StreamSource streamSource) throws TransformerConfigurationException
    {
        if (transformerFactory == null)
        {
            synchronized (XmlUtil.class)
            {
                if (transformerFactory == null)
                {
                    transformerFactory = TransformerFactory.newInstance();
                }
            }
        }
        return transformerFactory.newTemplates(streamSource);
    }

}
